package com.hongyun.tms.infra.excel.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hongyun.tms.common.constants.DefaultCreateByConstant;
import com.hongyun.tms.common.dto.mrate.MRateImportDto;
import com.hongyun.tms.common.dto.mrate.MRateSaveReqDto;
import com.hongyun.tms.common.enums.BizCode;
import com.hongyun.tms.common.utils.MapperingUtils;
import com.hongyun.tms.entity.MRate;
import com.hongyun.tms.exception.BusinessRuntimeException;
import com.hongyun.tms.service.IMRateService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 费率信息导入
 *
 * @author liPeng
 * @date 2024-09-10
 */
@Slf4j
@Component
public class MRateExcelListener extends DefaultExcelListener<MRateImportDto> {

    @Autowired
    private IMRateService rateService;

    public MRateExcelListener(IMRateService rateService) {
        this.rateService = rateService;
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        log.info("成功读取excel中【" + getRows().size() + "】条费率数据");

        // 获取读取的行数据并转为 MRateSaveReqDto 列表
        List<MRateImportDto> rows = getRows();
        List<MRateSaveReqDto> reqDtoList = MapperingUtils.copyList(rows, MRateSaveReqDto.class);

        // 校验每一条记录是否有重复
        preCheckBatch(reqDtoList);

        // 将 MRateSaveReqDto 转换为 MRate 实体对象
        List<MRate> mRateList = reqDtoList.stream().map(this::convertToMRate).collect(Collectors.toList());

        // 保存数据（可以进行批量插入）
        log.info("成功插入excel中【" + reqDtoList.size() + "】条费率数据");
        rateService.saveBatch(mRateList);  // 批量保存
    }
    /**
     * 将 MRateSaveReqDto 转换为 MRate 实体对象
     *
     * @param dto MRateSaveReqDto 对象
     * @return MRate 实体对象
     */
    private MRate convertToMRate(MRateSaveReqDto dto) {
        MRate mRate = MapperingUtils.copyProperties(dto, MRate.class);
        mRate.setCreateTime(LocalDateTime.now());
        mRate.setUpdateTime(LocalDateTime.now());
        mRate.setCreateBy(DefaultCreateByConstant.userId);
        mRate.setUpdateBy(DefaultCreateByConstant.userId);
        mRate.setCreateByName(DefaultCreateByConstant.userId.toString());
        mRate.setUpdateByName(DefaultCreateByConstant.userId.toString());
        return mRate;
    }


    /**
     * 批量校验导入的数据是否重复
     *
     * @param reqDtoList 导入的费率请求列表
     */
    private void preCheckBatch(List<MRateSaveReqDto> reqDtoList) {
        // 获取所有导入数据的 贸别 和 船舶类型
        List<String> tradeTypes = reqDtoList.stream().map(MRateSaveReqDto::getTradeType).collect(Collectors.toList());
        List<String> shipKindCodes = reqDtoList.stream().map(MRateSaveReqDto::getShipKindCode).collect(Collectors.toList());

        // 一次性查询数据库中可能重复的记录
        List<MRate> existingRates = findRatesByTradeTypeAndShipKind(tradeTypes, shipKindCodes);

        // 遍历导入数据进行校验
        for (int rowIndex = 0; rowIndex < reqDtoList.size(); rowIndex++) {
            MRateSaveReqDto dto = reqDtoList.get(rowIndex);

            // 筛选出相同贸别和船舶类型的记录
            List<MRate> sameList = existingRates.stream()
                    .filter(rate -> rate.getTradeType().equals(dto.getTradeType())
                            && rate.getShipKindCode().equals(dto.getShipKindCode()))
                    .collect(Collectors.toList());

            // 筛选出长度区间重叠的记录
            List<MRate> repeatList = sameList.stream()
                    .filter(x -> isLengthOverlap(dto.getLengthStart(), dto.getLengthEnd(), x.getLengthStart(), x.getLengthEnd()))
                    .collect(Collectors.toList());

            // 如果存在长度区间重复的记录，抛出异常并提示第几行数据重复
            if (CollectionUtils.isNotEmpty(repeatList)) {
                throw new BusinessRuntimeException(BizCode.TUG_EXIST_SAME_LENGTH,
                        String.format("第%d行，船长起止【%d-%d】已存在同类船舶数据，不能重复添加",
                                rowIndex + 1, dto.getLengthStart(), dto.getLengthEnd()));
            }
        }
    }

    /**
     * 判断两个长度区间是否有重叠
     *
     * @param dtoParamStart 前端传过来的要改动的对象的起始长度
     * @param dtoParamEnd   前端传过来的要改动的对象的结束长度
     * @param oldDataStart  数据表中已存在数据的起始长度
     * @param oldDataEnd    表中已存在数据的结束长度
     * @return 如果两个区间有重叠，则返回true，否则返回false
     */
    private boolean isLengthOverlap(int dtoParamStart, int dtoParamEnd, int oldDataStart, int oldDataEnd) {
        return (dtoParamStart >= oldDataStart && dtoParamStart < oldDataEnd)
                || (dtoParamEnd > oldDataStart && dtoParamEnd <= oldDataEnd)
                || (dtoParamStart < oldDataStart && dtoParamEnd > oldDataEnd);
    }

    public List<MRate> findRatesByTradeTypeAndShipKind(List<String> tradeTypes, List<String> shipKindCodes) {
        return rateService.list(new LambdaQueryWrapper<MRate>()
                .in(MRate::getTradeType, tradeTypes)
                .in(MRate::getShipKindCode, shipKindCodes)
        );
    }

}
